package de.jmda.sample.navigate;

import static de.jmda.core.mproc.ProcessingUtilities.asTypeElement;
import static de.jmda.core.mproc.ProcessingUtilities.getSubtypes;
import static de.jmda.core.mproc.ProcessingUtilities.getTypeElement;
import static de.jmda.core.mproc.ProcessingUtilities.isProcessingOver;
import static de.jmda.core.mproc.ProcessingUtilities.withEnclosedTypeElements;
import static de.jmda.core.util.CollectionsUtil.asSet;

import java.io.File;
import java.io.FileFilter;
import java.io.IOException;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

import javax.lang.model.element.TypeElement;

import de.jmda.core.mproc.task.AbstractTypeElementsTaskPackages;
import de.jmda.core.mproc.task.AbstractTypeElementsTaskTypes;
import de.jmda.core.mproc.task.TaskException;
import de.jmda.core.mproc.task.TaskRunner;
import de.jmda.core.util.classpath.ClasspathResource;
import de.jmda.core.util.classpath.ClasspathUtil;

public class Inheritance
{
	class Super {}
	class SubA extends Super {}
	class SubB extends Super {}

	static class NavigateUp extends AbstractTypeElementsTaskTypes
	{
		public NavigateUp(Set<? extends Class<?>> types)
    {
	    super(types);
    }

		@Override
    public boolean execute() throws TaskException
    {
			if (false == isProcessingOver())
			{
				TypeElement typeElementSubA = getTypeElement(SubA.class);
				TypeElement typeElementSuper = asTypeElement(typeElementSubA.getSuperclass());
				
				System.out.println(
						  "sub:   " + typeElementSubA.asType().toString() + "\n"
						+ "super: " + typeElementSuper.asType().toString());
			}

			return false;
    }		
	}
	
	static class NavigateDown extends AbstractTypeElementsTaskTypes
	{
		public NavigateDown(Set<? extends Class<?>> types)
    {
	    super(types);
    }

		@Override
    public boolean execute() throws TaskException
    {
			if (false == isProcessingOver())
			{
				Set<TypeElement> typeElements = withEnclosedTypeElements(getTypeElements());

				TypeElement typeElementSuper = getTypeElement(Super.class);
				
				System.out.println("sub types of " + typeElementSuper.asType());

				for (TypeElement typeElement : getSubtypes(typeElementSuper, typeElements))
				{
					System.out.println(typeElement.asType().toString());
				}
			}

			return false;
    }		
	}

	static class NavigateDownWithTaskPackages extends AbstractTypeElementsTaskPackages
	{
		private Class<?> superType;

		public NavigateDownWithTaskPackages(Set<Package> packages, Class<?> superType)
    {
			super(packages);
			this.superType = superType;
    }

		@Override
    public boolean execute() throws TaskException
    {
			if (false == isProcessingOver())
			{
				Set<TypeElement> typeElements = withEnclosedTypeElements(getTypeElements());

				TypeElement typeElementSuper = getTypeElement(superType);
				
				System.out.println("sub types of " + typeElementSuper.asType());

				for (TypeElement typeElement : getSubtypes(typeElementSuper, typeElements))
				{
					System.out.println(typeElement.asType().toString());
				}
				
				System.out.println("found " + getSubtypes(typeElementSuper, typeElements).size() + " sub types of " + typeElementSuper.asType());
			}

			return false;
    }		
	}

	private static void navigateUp() throws IOException
  {
		NavigateUp task = new NavigateUp(asSet(SubA.class));
		TaskRunner.run(task);
  }

	private static void navigateDown() throws IOException
  {
		NavigateDown task = new NavigateDown(asSet(Inheritance.class));
		TaskRunner.run(task);
  }

	private static void navigateDownWithTaskPackages()
			throws
					IOException,
					InstantiationException,
					IllegalAccessException,
					ClassNotFoundException
  {
//		System.out.println("classpath entries");
//		for (String s : new TreeSet<>(asSet(RuntimeUtil.JAVA_CLASS_PATH.split(";"))))
//		{
//			System.out.println(s);
//		}
		FileFilter ignoreInResourceContainers =
				new FileFilter()
				{
					@Override
					public boolean accept(File f)
					{
						return
								false ==
								(
//										   f.getPath().contains("classmate-0.8.0.jar")
										 f.getPath().contains("guava-15.0.jar")
										  || f.getPath().contains("guava-14.0-rc1.jar")
//										|| f.getPath().contains("commons-codec-1.5.jar")
//										|| f.getPath().contains("commons-collections-3.2.1.jar")
										|| f.getPath().contains("commons-configuration-1.9.jar")
//										|| f.getPath().contains("commons-io-2.4.jar")
//										|| f.getPath().contains("commons-lang-2.6.jar")
//										|| f.getPath().contains("commons-logging-1.1.1.jar")
//										|| f.getPath().contains("dom4j-1.6.1.jar")
//										|| f.getPath().contains("validation-api-1.0.0.GA.jar")
//										|| f.getPath().contains("validation-api-1.1.0.Final.jar")
//										|| f.getPath().contains("junit-4.11.jar")
										|| f.getPath().contains("log4j-1.2.17.jar")
//										|| f.getPath().contains("commons-lang3-3.1.jar")
//										|| f.getPath().contains("poi-ooxml-schemas-3.9.jar")
//										|| f.getPath().contains("poi-ooxml-3.9.jar")
//										|| f.getPath().contains("poi-3.9.jar")
										|| f.getPath().contains("xmlbeans-2.3.0.jar")
//										|| f.getPath().contains("javax.persistence-2.0.0.jar")
//										|| f.getPath().contains("hamcrest-core-1.3.jar")
										|| f.getPath().contains("hibernate-validator-5.0.1.Final.jar")
//										|| f.getPath().contains("jboss-logging-3.1.1.GA.jar")
//										|| f.getPath().contains("stax-api-1.0.1.jar")
//										|| f.getPath().contains("xml-apis-1.0.b2.jar")
//										|| f.getPath().contains("jmda.core\bin")
//										|| f.getPath().contains("jmda.gen\bin")
//										|| f.getPath().contains("jmda.sample\bin")
								);
					}
				};

		Map<String, ClasspathResource> packagesAsClasspathResources =
				ClasspathUtil.getClasspathResources(
						ignoreInResourceContainers,
						ClasspathUtil.FILTER_ACCEPT_FILES_DIRECTORIES_ONLY,
						ClasspathUtil.FILTER_ACCEPT_ZIP_ENTRIES_DIRECTORIES_ONLY);
		
		Map<String, Package> packagesByName =
				ClasspathUtil.getPackages(packagesAsClasspathResources.keySet());

		NavigateDownWithTaskPackages task =
				new NavigateDownWithTaskPackages(
						new HashSet<>(packagesByName.values()), Object.class);
//						new HashSet<>(packagesByName.values()), Serializable.class);

		TaskRunner.run(task);
  }

	public static void main(String[] args)
			throws
					IOException,
					InstantiationException,
					IllegalAccessException,
					ClassNotFoundException
  {
	  navigateUp();
	  navigateDown();
	  navigateDownWithTaskPackages();
  }
}